package com.enation.app.javashop.core.aftersale.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.dag.eagleshop.core.account.model.dto.account.*;
import com.dag.eagleshop.core.account.model.enums.AccountTypeEnum;
import com.dag.eagleshop.core.account.model.enums.TradeSubjectEnum;
import com.dag.eagleshop.core.account.model.enums.TransferTypeEnum;
import com.dag.eagleshop.core.account.service.AccountManager;
import com.dag.eagleshop.core.dict.service.DictService;
import com.enation.app.javashop.core.aftersale.model.dos.ClaimsDO;
import com.enation.app.javashop.core.aftersale.model.dos.ExceptionOrderDO;
import com.enation.app.javashop.core.aftersale.model.dto.ClaimSkusDTO;
import com.enation.app.javashop.core.aftersale.model.dto.ExceptionOrderDTO;
import com.enation.app.javashop.core.aftersale.model.dto.SpreadRefundDTO;
import com.enation.app.javashop.core.aftersale.model.enums.*;
import com.enation.app.javashop.core.aftersale.model.vo.*;
import com.enation.app.javashop.core.aftersale.service.AfterSaleManager;
import com.enation.app.javashop.core.aftersale.service.ClaimsManager;
import com.enation.app.javashop.core.aftersale.service.ExceptionOrderManager;
import com.enation.app.javashop.core.base.model.vo.SmsSendVO;
import com.enation.app.javashop.core.base.rabbitmq.AmqpExchange;
import com.enation.app.javashop.core.base.service.SmsManager;
import com.enation.app.javashop.core.member.model.dos.Member;
import com.enation.app.javashop.core.member.model.dos.MemberCoupon;
import com.enation.app.javashop.core.member.service.MemberManager;
import com.enation.app.javashop.core.promotion.coupon.model.dos.CouponDO;
import com.enation.app.javashop.core.promotion.coupon.model.dos.CouponTypeEnum;
import com.enation.app.javashop.core.promotion.coupon.service.CouponManager;
import com.enation.app.javashop.core.shop.model.dos.Clerk;
import com.enation.app.javashop.core.shop.service.ClerkManager;
import com.enation.app.javashop.framework.context.UserContext;
import com.enation.app.javashop.framework.database.DaoSupport;
import com.enation.app.javashop.framework.database.Page;
import com.enation.app.javashop.framework.entity.DictDTO;
import com.enation.app.javashop.framework.exception.ServiceException;
import com.enation.app.javashop.framework.security.model.Seller;
import com.enation.app.javashop.framework.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author: zhou
 * @Date: 2020/9/21
 * @Description: 理赔管理
 */
@Service
public class ClaimsManagerImpl implements ClaimsManager {
    @Autowired
    @Qualifier("tradeDaoSupport")
    private DaoSupport daoSupport;
    @Autowired
    private ClerkManager clerkManager;
    @Autowired
    private ExceptionOrderManager exceptionOrderManager;
    @Autowired
    private AccountManager accountManager;
    @Autowired
    private MemberManager memberManager;
    @Autowired
    private AfterSaleManager afterSaleManager;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private CouponManager couponManager;
    @Autowired
    private DictService dictService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private SmsManager smsManager;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final String claimSnPrefix = "LP";

    private final String couponLimitPrefix = "COUPON_SETTING_";

    /**
     * 根据条件查询理赔单
     *
     * @param claimsQueryVO 查询实体类
     * @return
     */
    @Override
    public Page<ClaimsVO> getClaimsList(ClaimsQueryVO claimsQueryVO, Seller seller) {
        if (seller == null || seller.getSellerId() == null) {
            throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), "您当前没有店铺");
        }
        String sql = "select " + this.sqlParam() + ", o.shipping_type,o.ship_name,o.ship_mobile,o.ship_addr,\n" +
                "eo.member_nickname,eo.member_realname_lv1,eo.member_mobile_lv1,eo.site_name,eo.site_leader_name,eo.site_leader_mobile\n" +
                "from es_claims c left join es_exception_order eo on c.exception_id=eo.exception_id\n" +
                "left join es_order o on c.order_sn=o.sn\n" +
                "where o.seller_id= " + seller.getSellerId() + "\n";
        List<Object> param = new ArrayList<>();

        //时间范围查询
        Long startTime = claimsQueryVO.getStartTime();
        Long endTime = claimsQueryVO.getEndTime();
        String timeType = claimsQueryVO.getTimeType();
        if (startTime != null && startTime > 0) {
            if (StringUtil.isEmpty(timeType)) {
                throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), "请选择时间类型");
            }
            sql = sql.concat(" and c." + timeType + ">=? ");
            param.add(startTime);
        }
        if (endTime != null && endTime > 0) {
            if (StringUtil.isEmpty(timeType)) {
                throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), "请选择时间类型");
            }
            sql = sql.concat(" and c." + timeType + "<=? ");
            param.add(endTime);
        }
        //理赔单号
        if (StringUtil.notEmpty(claimsQueryVO.getClaimSn())) {
            sql = sql.concat(" and c.claim_sn=? ");
            param.add(claimsQueryVO.getClaimSn());
        }
        //异常单号
        if (StringUtil.notEmpty(claimsQueryVO.getExceptionSn())) {
            sql = sql.concat(" and c.exception_sn=? ");
            param.add(claimsQueryVO.getExceptionSn());
        }
        //订单号
        if (StringUtil.notEmpty(claimsQueryVO.getOrderSn())) {
            sql = sql.concat(" and c.order_sn=? ");
            param.add(claimsQueryVO.getOrderSn());
        }
        //买家昵称
        if (StringUtil.notEmpty(claimsQueryVO.getMemberNickname())) {
            sql = sql.concat(" and eo.member_nickname like ? ");
            param.add("%" + claimsQueryVO.getMemberNickname() + "%");
        }
        //理赔状态
        if (StringUtil.notEmpty(claimsQueryVO.getClaimStatus())) {
            sql = sql.concat(" and c.claim_status=? ");
            param.add(claimsQueryVO.getClaimStatus());
        }
        //理赔类型
        if (StringUtil.notEmpty(claimsQueryVO.getClaimType())) {
            sql = sql.concat(" and c.claim_type=? ");
            param.add(claimsQueryVO.getClaimType());
        }
        //一级团长真实姓名
        if (StringUtil.notEmpty(claimsQueryVO.getMemberRealnameLv1())) {
            sql = sql.concat(" and eo.member_realname_lv1 like ? ");
            param.add("%" + claimsQueryVO.getMemberRealnameLv1() + "%");
        }
        //站点名
        if (StringUtil.notEmpty(claimsQueryVO.getSiteName())) {
            sql = sql.concat(" and eo.site_name like ? ");
            param.add("%" + claimsQueryVO.getSiteName() + "%");
        }
        sql = sql.concat(" order by c.apply_time desc ");
        logger.debug("查询异常单：" + sql);
        Page<ClaimsVO> page = this.daoSupport.queryForPage(sql, claimsQueryVO.getPageNo(), claimsQueryVO.getPageSize(), ClaimsVO.class, param.toArray());
        return page;
    }

    /**
     * 批量添加理赔单
     *
     * @param
     */
    @Override
    @Transactional(value = "tradeTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Result<String> addClaims(ClaimsParamVO claimsParamVO, Seller seller) {
        //检验异常单是否生成过理赔单
        try {
            exceptionOrderManager.checkAbleProcess(claimsParamVO.getExceptionId());
        } catch (ServiceException s) {
            return new Result(ExceptionClaimsCodeEnum.valueOf(s.getCode()).getCode(), s.getMessage());
        }

        //获取申请理赔单的店员信息
        Clerk clerk = this.getClerk(seller.getUid());
        //需要保存的理赔单
        List<ClaimsDO> claimsDOList = new ArrayList<>();
        //存储异常订单表中的解决方式
        List<String> claimsTypeList = new ArrayList<>();
        String describe = claimsParamVO.getClaimDescribe();
        //保存库存不足的商品名
        StringBuilder resultData = new StringBuilder();
        ClaimsVO compensationClaim = claimsParamVO.getCompensationClaim();
        if (compensationClaim != null && compensationClaim.getClaimType() != null) {
            ClaimsDO claimsDO = this.initClaim(compensationClaim, clerk, describe, resultData);
            if (claimsDO != null && StringUtil.notEmpty(claimsDO.getClaimSkus())) {
                //存储异常订单表中的解决方式
                claimsTypeList.add(ClaimsTypeEnum.valueOf(claimsDO.getClaimType()).description());
                claimsDOList.add(claimsDO);
            }
        }
        ClaimsVO freeClaim = claimsParamVO.getFreeClaim();
        if (freeClaim != null && freeClaim.getClaimType() != null) {
            ClaimsDO claimsDO = this.initClaim(freeClaim, clerk, describe, resultData);
            if (claimsDO != null && StringUtil.notEmpty(claimsDO.getClaimSkus())) {
                //存储异常订单表中的解决方式
                claimsTypeList.add(ClaimsTypeEnum.valueOf(claimsDO.getClaimType()).description());
                claimsDOList.add(claimsDO);
            }
        }
        ClaimsVO paymentClaim = claimsParamVO.getPaymentClaim();
        if (paymentClaim != null && paymentClaim.getClaimType() != null) {
            ClaimsDO claimsDO = this.initClaim(paymentClaim, clerk, describe, resultData);
            //存储异常订单表中的解决方式
            claimsTypeList.add(ClaimsTypeEnum.valueOf(claimsDO.getClaimType()).description());
            claimsDOList.add(claimsDO);
        }
        ClaimsVO refundClaim = claimsParamVO.getRefundClaim();
        if (refundClaim != null && refundClaim.getClaimType() != null) {
            ClaimsDO claimsDO = this.initClaim(refundClaim, clerk, describe, resultData);
            //存储异常订单表中的解决方式
            claimsTypeList.add(ClaimsTypeEnum.valueOf(claimsDO.getClaimType()).description());
            claimsDOList.add(claimsDO);
        }
        ClaimsVO couponClaim = claimsParamVO.getCouponClaim();
        if (couponClaim != null && couponClaim.getClaimType() != null) {
            ClaimsDO claimsDO = this.initClaim(couponClaim, clerk, describe, resultData);
            if (claimsDO != null) {
                //存储异常订单表中的解决方式
                claimsTypeList.add(ClaimsTypeEnum.valueOf(claimsDO.getClaimType()).description());
                claimsDOList.add(claimsDO);
            }
        }
        ClaimsVO otherClaim = claimsParamVO.getOtherClaim();
        if (otherClaim != null && otherClaim.getClaimType() != null) {
            ClaimsDO claimsDO = this.initClaim(otherClaim, clerk, describe, resultData);
            //存储异常订单表中的解决方式
            claimsTypeList.add(ClaimsTypeEnum.valueOf(claimsDO.getClaimType()).description());
            claimsDOList.add(claimsDO);
        }

        if (!StringUtil.isEmpty(resultData.toString())) {
            return new Result(ExceptionClaimsCodeEnum.E616.getCode(), ExceptionClaimsCodeEnum.E616.getDescription(), resultData);
        }

        //如果理赔单不为空，批量添加理赔单
        if (!CollectionUtils.isEmpty(claimsDOList)) {
            this.daoSupport.batchInsert("es_claims", claimsDOList);

            // 处理异常单后，添加理赔方式，更新异常单状态，处理人信息，解决说明和解决方式
            ExceptionOrderDTO exceptionOrderDTO = new ExceptionOrderDTO();
            exceptionOrderDTO.setExceptionId(claimsParamVO.getExceptionId());
            exceptionOrderDTO.setProcessId(clerk.getClerkId());
            exceptionOrderDTO.setProcessName(clerk.getClerkName());
            exceptionOrderDTO.setProcessTime(DateUtil.getDateline());
            //组装异常单解决方式
            String claimsTypeString = StringUtil.implode("+", claimsTypeList.toArray());
            exceptionOrderDTO.setSolveTypes(claimsTypeString);
            exceptionOrderDTO.setSolveExplain(claimsParamVO.getClaimDescribe());
            exceptionOrderManager.updateSolve(exceptionOrderDTO);
        } else {
            return new Result(ExceptionClaimsCodeEnum.E614.getCode(), "当前没有可保存的理赔单！");
        }

        return Result.successMessage();
    }

    /**
     * 批量审核理赔单，记录审核信息
     *
     * @param claimsIds     理赔单ID
     * @param claimsStatus  审核状态 AUDIT_SUCCESS("审核通过"),AUDIT_FAIL("审核失败"),
     * @param auditDescribe 审核描述
     * @param seller        审核人信息
     */
    @Override
    public void auditClaims(List<Integer> claimsIds, String claimsStatus, String auditDescribe, Seller seller) {
        if (CollectionUtils.isEmpty(claimsIds) || StringUtil.isEmpty(claimsStatus) || seller == null) {
            throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), "获取参数异常");
        }

        // 根据理赔单ID获取理赔单
        List<ClaimsDO> claimsDOList = this.getListByClaimsId(claimsIds);

        //1、判断理赔单状态，所有理赔单状态都是已提交时才可以审核
        if (!CollectionUtils.isEmpty(claimsDOList)) {
            for (ClaimsDO claimsDO : claimsDOList) {
                if (!claimsDO.getClaimStatus().equals(ClaimsStatusEnum.SUBMIT.value())) {
                    throw new ServiceException(ExceptionClaimsCodeEnum.E621.value(), ExceptionClaimsCodeEnum.E621.getDescription());
                }
            }
        } else {
            throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), ExceptionClaimsCodeEnum.E614.getDescription());
        }
        //2、调用实际的审核流程，并改变理赔单状态
        this.actualAudit(claimsDOList, claimsStatus, auditDescribe, seller);

        //3、审核状态改变后发送通知
        this.sendMq(claimsDOList,claimsStatus);
    }

    /**
     * 改变理赔状态,记录理赔信息
     *
     * @param claimsIds     理赔单IDs
     * @param claimsStatus  理赔状态 CLAIM_SUCCESS("理赔成功"),CLAIM_FAIL("理赔失败");
     * @param claimDescribe 理赔备注
     * @param seller        理赔人信息
     */
    @Override
    public void claimsStatus(List<Integer> claimsIds, String claimsStatus, String claimDescribe, Seller seller) {
        //判断参数是否为空
        if (CollectionUtils.isEmpty(claimsIds) || StringUtil.isEmpty(claimsStatus) || seller == null) {
            throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), ExceptionClaimsCodeEnum.E614.getDescription());
        }

        // 1、根据理赔单ID获取理赔单
        List<ClaimsDO> claimsDOList = this.getListByClaimsId(claimsIds);
        //判断理赔单是否可以撤销
        if (!CollectionUtils.isEmpty(claimsDOList)) {
            for (ClaimsDO claimsDO : claimsDOList) {
                String claimStatus = claimsDO.getClaimStatus();
                String claimType = claimsDO.getClaimType();
                //只有赔付现金和多退少补才判断时候已经审核通过
                if (claimType.equals(ClaimsTypeEnum.PAYMENT_CASH.value()) || claimType.equals(ClaimsTypeEnum.REFUND_CASH.value())) {
                    //如果理赔单状态为审核通过或理赔成功，不能撤销理赔单
                    if (claimStatus.equals(ClaimsStatusEnum.AUDIT_SUCCESS.value()) || claimStatus.equals(ClaimsStatusEnum.CLAIM_SUCCESS.value())) {
                        throw new ServiceException(ExceptionClaimsCodeEnum.E622.value(), "理赔单：【" + claimsDO.getClaimSn() + "】" + ExceptionClaimsCodeEnum.E622.getDescription());
                    }
                }

            }
        }

        //2、改变理赔单状态，添加理赔信息
        //需要改变理赔状态的理赔单ID
        Object[] claimsIdsArray = claimsIds.toArray();
        String claimsIdsString = StringUtil.implode(",", claimsIdsArray);

        //获取申请理赔单的店员信息
        Clerk clerk = this.getClerk(seller.getUid());
        //更新理赔人及理赔状态
        String sql = "update es_claims set claim_status = '" + claimsStatus + "', claim_member_id=" + clerk.getClerkId() + ", claim_time=" + DateUtil.getDateline() +
                ", claim_member_name='" + clerk.getClerkName() + "'";
        if (StringUtil.notEmpty(claimDescribe)) {
            sql = sql.concat(", claim_describe='" + claimDescribe + "'");
        }
        sql = sql.concat(" where claim_id in(" + claimsIdsString + ")");
        this.daoSupport.execute(sql);

        // 理赔单改变状态发MQ
        this.sendMq(claimsDOList,claimsStatus);
    }

    /**
     * 通过异常单ID获取理赔单
     *
     * @param exceptionId 异常单ID
     */
    @Override
    public List<ClaimsDO> getClaimsByExceptionId(Integer exceptionId) {
        if (exceptionId == null) {
            throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), "理赔单ID为空，无法查询理赔单。");
        }
        String sql = "select " + this.sqlParam() + "from es_claims c where c.exception_id = ? \n";
        return this.daoSupport.queryForList(sql, ClaimsDO.class, exceptionId);
    }

    /**
     * 给前端返回封装好的数据
     *
     * @param exceptionId 异常单ID
     */
    @Override
    public ClaimsParamVO getClaimsParamVOByExceptionId(Integer exceptionId) {
        String sql = "select " + this.sqlParam() + "from es_claims c where c.exception_id = ? \n";
        List<ClaimsDO> claimsDOList = this.daoSupport.queryForList(sql, ClaimsDO.class, exceptionId);
        //转换前端展示的数据格式
        ClaimsParamVO paramVO = new ClaimsParamVO();
        if (!CollectionUtils.isEmpty(claimsDOList)) {
            ClaimsDO claimsDO = claimsDOList.get(0);
            paramVO.setExceptionId(exceptionId);
            paramVO.setExceptionSn(claimsDO.getExceptionSn());
            paramVO.setOrderSn(claimsDO.getOrderSn());
            paramVO.setClaimDescribe(claimsDO.getClaimDescribe());
            Map<String, ClaimsDO> claimsDOMap = claimsDOList.stream().collect(Collectors.toMap(ClaimsDO::getClaimType, Function.identity()));

            //补发商品
            ClaimsDO compensationGoods = claimsDOMap.get(ClaimsTypeEnum.COMPENSATION_GOODS.value());
            if (compensationGoods != null) {
                ClaimsVO claimsVO = new ClaimsVO();
                BeanUtil.copyProperties(compensationGoods, claimsVO);
                if (claimsVO.getDeliveryTime() != null)
                    claimsVO.setDeliveryTimeString(DateUtil.toString(claimsVO.getDeliveryTime(), "yyyy-MM-dd"));
                claimsVO.setSkuList(JSON.parseObject(compensationGoods.getClaimSkus(), new TypeReference<List<ClaimSkusDTO>>() {
                }));
                paramVO.setCompensationClaim(claimsVO);
            }

            //赠送礼品
            ClaimsDO freeGift = claimsDOMap.get(ClaimsTypeEnum.FREE_GIFT.value());
            if (freeGift != null) {
                ClaimsVO claimsVO = new ClaimsVO();
                BeanUtil.copyProperties(freeGift, claimsVO);
                if (claimsVO.getDeliveryTime() != null)
                    claimsVO.setDeliveryTimeString(DateUtil.toString(claimsVO.getDeliveryTime(), "yyyy-MM-dd"));
                claimsVO.setSkuList(JSON.parseObject(freeGift.getClaimSkus(), new TypeReference<List<ClaimSkusDTO>>() {
                }));
                paramVO.setFreeClaim(claimsVO);
            }

            //赔付现金
            ClaimsDO paymentCash = claimsDOMap.get(ClaimsTypeEnum.PAYMENT_CASH.value());
            if (paymentCash != null) {
                ClaimsVO claimsVO = new ClaimsVO();
                BeanUtil.copyProperties(paymentCash, claimsVO);
                paramVO.setPaymentClaim(claimsVO);
            }

            //多退少补
            ClaimsDO refundCash = claimsDOMap.get(ClaimsTypeEnum.REFUND_CASH.value());
            if (refundCash != null) {
                ClaimsVO claimsVO = new ClaimsVO();
                BeanUtil.copyProperties(refundCash, claimsVO);
                paramVO.setRefundClaim(claimsVO);
            }

            //赔付售后券
            ClaimsDO coupon = claimsDOMap.get(ClaimsTypeEnum.AFTERSALE_COUPON.value());
            if (coupon != null) {
                ClaimsVO claimsVO = new ClaimsVO();
                BeanUtil.copyProperties(coupon, claimsVO);
                logger.debug(coupon.getClaimSkus());
                if (!StringUtil.isEmpty(coupon.getClaimSkus())) {
                    claimsVO.setCouponDO(JSONObject.parseObject(coupon.getClaimSkus(), CouponDO.class));
                }
                paramVO.setCouponClaim(claimsVO);
            }

            //其它
            ClaimsDO other = claimsDOMap.get(ClaimsTypeEnum.OTHER.value());
            if (other != null) {
                ClaimsVO claimsVO = new ClaimsVO();
                BeanUtil.copyProperties(other, claimsVO);
                paramVO.setOtherClaim(claimsVO);
            }
        }
        return paramVO;
    }

    /**
     * 异常单撤销之后，撤销理赔单,记录理赔信息
     *
     * @param exceptionIds 异常单IDs
     * @param seller       理赔人
     */
    @Override
    public void auditClaimsByExcpIds(Integer[] exceptionIds, Seller seller) {
        if (exceptionIds == null || seller == null) {
            throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), ExceptionClaimsCodeEnum.E614.getDescription());
        }
        String claimDescribe = "异常单撤销";
        String exceptionIdString = StringUtil.implode(",", exceptionIds);
        //获取申请理赔单的店员信息
        Clerk clerk = this.getClerk(seller.getUid());
        //更新理赔人及理赔状态
        String sql = "update es_claims set claim_status = '" + ClaimsStatusEnum.CANCELLED.value() + "', claim_member_id=" + clerk.getClerkId() + ", claim_time=" + DateUtil.getDateline() +
                ", claim_member_name='" + clerk.getClerkName() + "', claim_describe='" + claimDescribe + "' where exception_id in (" + exceptionIdString + ")";
        daoSupport.execute(sql);

        String querySql="select "+this.sqlParam() +" from es_claims c where exception_id in (" + exceptionIdString + ")";
        List<ClaimsDO> claimsDOList=daoSupport.queryForList(querySql,ClaimsDO.class);

        // 理赔单改变状态发MQ
        this.sendMq(claimsDOList,ClaimsStatusEnum.CANCELLED.value());
    }

    //检测订单是否可以赠送售后券
    @Override
    public List<CouponDO> getAftersaleCoupon(String orderSn) {

        //可以理赔的优惠券列表
        List<CouponDO> couponDOList = new ArrayList<>();

        //当前店铺
        Seller seller = UserContext.getSeller();
        if (seller.getSellerId() != null) {
            //1、查询当前订单已经送过几张售后券
            String claimSql = "select " + this.sqlParam() + "from es_claims c where c.order_sn = '" + orderSn +
                    "' and c.claim_type = '" + ClaimsTypeEnum.AFTERSALE_COUPON.value()
                    + "' and c.claim_status not in ('"+ClaimsStatusEnum.AUDIT_SUCCESS.value()+"','"+ClaimsStatusEnum.CLAIM_SUCCESS.value()+"')";
            List<ClaimsDO> claimsDOList = this.daoSupport.queryForList(claimSql, ClaimsDO.class);

            //是否返回优惠券信息
            Boolean status = true;

            //2、如果已经理赔过售后券，判断售后券领取限制
            if (!CollectionUtils.isEmpty(claimsDOList)) {
                //2.1  获取售后券的配置
                Integer limitNum = this.getLimitNum();
                //2.2  如果店铺配置的售后券限制领取数量大于已经发放的售后券数量，就返回优惠券信息
                if (limitNum <= claimsDOList.size()) {
                    status = false;
                }
            }

            //3、如果没有领取过优惠券或没有超出限制，返回优惠券信息
            if (status) {
                String couponSql = "select * from es_coupon where " + DateUtil.getDateline() + " between start_time and end_time\n" +
                        "and create_num-received_num>0 and seller_id=" + seller.getSellerId() + " and coupon_type='" + CouponTypeEnum.AFTERSALE.value() + "'";
                couponDOList = this.daoSupport.queryForList(couponSql, CouponDO.class);
            }
        }

        return couponDOList;
    }

    //获取店铺配置的订单理赔售后券限制数量，如果没有配置，默认为1
    private Integer getLimitNum() {
        Integer limitNum = 1;
        //获取拥有当前店铺的用户
        Member member = memberManager.getModel(UserContext.getBuyer().getUid());
        if (member != null && !StringUtil.isEmpty(member.getMobile())) {
            //获取数据字典
            List<DictDTO> dictDTOList = DictUtils.getDictList(null, couponLimitPrefix + member.getMobile());
            if (!CollectionUtils.isEmpty(dictDTOList)) {
                String limitNumString = dictDTOList.get(0).getValue();
                limitNum = Integer.parseInt(limitNumString);
            }
        }
        return limitNum;
    }

    /**
     * 通过理赔单ID获取理赔单
     *
     * @param claimsIds 理赔单ID
     */
    private List<ClaimsDO> getListByClaimsId(List<Integer> claimsIds) {
        if (CollectionUtils.isEmpty(claimsIds)) {
            throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), "理赔单ID为空，无法查询理赔单。");
        }
        String claimsIdString = StringUtil.implode(",", claimsIds.toArray());
        String sql = "select " + this.sqlParam() + " from es_claims c where claim_id in(" + claimsIdString + ")";
        return this.daoSupport.queryForList(sql, ClaimsDO.class);
    }

    //审核的实际流程 因为多退少补为第三方接口，不能使用事务
    private void actualAudit(List<ClaimsDO> claimList, String claimsStatus, String auditDescribe, Seller seller) {
        //获取申请理赔单的店员信息
        Clerk clerk = this.getClerk(seller.getUid());


        List<Integer> exceptionIds = claimList.stream().map(ClaimsDO::getExceptionId).collect(Collectors.toList());
        List<ExceptionOrderDO> exceptionOrderDOList = exceptionOrderManager.queryListByIds(exceptionIds.toArray(new Integer[exceptionIds.size()]));
        Map<Integer, ExceptionOrderDO> exceptionOrderDOMap = exceptionOrderDOList.stream().collect(Collectors.toMap(ExceptionOrderDO::getExceptionId, Function.identity()));
        for (ClaimsDO claimsDO : claimList) {
            //1、只有审核通过 且理赔类型为赔付现金、多退少补、发放收货券时才能走实际的赔付流程
            if (claimsStatus.equals(ClaimsStatusEnum.AUDIT_SUCCESS.value())) {
                String claimType = claimsDO.getClaimType();
                //赔付现金
                if (ClaimsTypeEnum.PAYMENT_CASH.value().equals(claimType)) {
                    Member member = memberManager.getModel(claimsDO.getMemberId());
                    String accountMemberId = member.getAccountMemberId();
                    //判断退款账户是否存在
                    if (StringUtil.notEmpty(accountMemberId)) {
                        // 发起订单结算业务转账
                        TransferReqDTO transferReqDTO = new TransferReqDTO();
                        transferReqDTO.setTradeVoucherNo(claimsDO.getClaimSn());

                        // 查询平台账户和收益账户
                        AccountDTO platformAccountDTO = accountManager.queryPlatformAccount(AccountTypeEnum.PLATFORM.getIndex());
                        //付款人
                        transferReqDTO.setDraweeMemberId(platformAccountDTO.getMemberId());
                        transferReqDTO.setDraweeAccountType(platformAccountDTO.getAccountType());
                        //收款人
                        transferReqDTO.setPayeeMemberId(accountMemberId);
                        transferReqDTO.setPayeeAccountType(AccountTypeEnum.MEMBER_MASTER.getIndex());
                        //付款金额
                        transferReqDTO.setAmount(BigDecimal.valueOf(claimsDO.getClaimPrice()));
                        transferReqDTO.setTradeSubject(TradeSubjectEnum.EXCEPTION_ORDER_PAYMENT_CASH.description());
                        transferReqDTO.setTradeContent("订单" + claimsDO.getOrderSn() + "发生异常赔付现金");
                        transferReqDTO.setTransferType(TransferTypeEnum.AT_ONCE.getIndex());
                        transferReqDTO.setOperatorId("1");
                        transferReqDTO.setOperatorName("系统");
                        logger.info("发起转账:" + JSON.toJSON(transferReqDTO).toString());
                        TransferRespDTO requestTransferRespDTO = accountManager.transfer(transferReqDTO);
                        logger.info("转账结果:" + JSON.toJSON(requestTransferRespDTO).toString());
                        if (!requestTransferRespDTO.isSuccess()) {
                            throw new ServiceException(ExceptionClaimsCodeEnum.E619.value(), requestTransferRespDTO.getMessage());
                        } else {
                            // 生产环境发短息
                            if (EnvironmentUtils.isProd()) {
                                //异常描述 页面必填
                                String exceptionDescription = exceptionOrderDOMap.get(claimsDO.getExceptionId()).getExceptionDescription();
                                SmsSendVO smsSendVO = new SmsSendVO();
                                smsSendVO.setContent("【智溢商城】您的订单" + claimsDO.getOrderSn() + "因" +
                                        exceptionDescription + "产生现金理赔，款项已转入智溢商城钱包，请注意查收！");
                                smsSendVO.setMobile(member.getMobile());
                                //发送短信
                                amqpTemplate.convertAndSend(AmqpExchange.SEND_MESSAGE, AmqpExchange.SEND_MESSAGE + "_QUEUE", smsSendVO);
                            }
                        }
                    } else {
                        throw new ServiceException(ExceptionClaimsCodeEnum.E623.value(), ExceptionClaimsCodeEnum.E623.getDescription());
                    }

                }
                //多退少补
                if (ClaimsTypeEnum.REFUND_CASH.value().equals(claimType)) {
                    SpreadRefundDTO spreadRefundDTO = new SpreadRefundDTO();
                    spreadRefundDTO.setRefundType(RefundTypeEnum.SPREAD_REFUND.value());
                    spreadRefundDTO.setFixRefund(claimsDO.getClaimPrice());
                    spreadRefundDTO.setRefundReason(claimsDO.getRefundReason());
                    //退款原因
                    String refundDescription = claimsDO.getAuditDescribe() == null ? "理赔单审核通过" : claimsDO.getAuditDescribe();
                    spreadRefundDTO.setRefundDescription(refundDescription);
                    String[] orderSnList = {claimsDO.getOrderSn()};
                    List<String> result = afterSaleManager.spreadRefund(orderSnList, spreadRefundDTO, seller);
                    String resultString = result.get(0);
                    if (!resultString.contains("退款成功")) {
                        throw new ServiceException(ExceptionClaimsCodeEnum.E620.value(), resultString);
                    }
                }
                //发放售后券
                if (ClaimsTypeEnum.AFTERSALE_COUPON.value().equals(claimType)) {
                    //检测是否可以发放售后券
                    List<CouponDO> couponDOList = this.getAftersaleCoupon(claimsDO.getOrderSn());
                    if (CollectionUtils.isEmpty(couponDOList) && StringUtil.notEmpty(claimsDO.getClaimSkus())) {
                        CouponDO couponDO = JSON.parseObject(claimsDO.getClaimSkus(), new TypeReference<CouponDO>() {
                        });
                        Integer couponId = couponDO.getCouponId();

                        // 1、添加会员优惠券表
                        MemberCoupon memberCoupon = new MemberCoupon();
                        memberCoupon.setCouponId(couponId);
                        memberCoupon.setTitle(couponDO.getTitle());
                        memberCoupon.setCreateTime(DateUtil.getDateline());
                        memberCoupon.setMemberId(claimsDO.getMemberId());
                        memberCoupon.setStartTime(couponDO.getStartTime());
                        memberCoupon.setEndTime(couponDO.getEndTime());
                        memberCoupon.setCouponPrice(couponDO.getCouponPrice());
                        memberCoupon.setCouponThresholdPrice(couponDO.getCouponThresholdPrice());
                        memberCoupon.setSellerId(couponDO.getSellerId());
                        memberCoupon.setUsedStatus(0);
                        memberCoupon.setSellerName(couponDO.getSellerName());
                        this.daoSupport.insert(memberCoupon);

                        //2、修改优惠券已被领取的数量
                        couponManager.addReceivedNum(couponId);
                    }

                }
            }

            //2、更新审核人及审核状态
            String sql = "update es_claims set claim_status = '" + claimsStatus + "', audit_member_id=" + clerk.getClerkId() + ", audit_time=" + DateUtil.getDateline() +
                    ", audit_member_name='" + clerk.getClerkName() + "'";
            if (StringUtil.notEmpty(auditDescribe)) {
                sql = sql.concat(", audit_describe='" + auditDescribe + "'");
            }
            sql = sql.concat(" where claim_id =" + claimsDO.getClaimId());
            this.daoSupport.execute(sql);
        }
    }

    /**
     * @param claimsVO      前端传过来的参数
     * @param clerk         店员信息
     * @param claimDescribe 解决说明
     * @param resultData    库存不足的商品名
     * @return 要保存的理赔单
     */
    private ClaimsDO initClaim(ClaimsVO claimsVO, Clerk clerk, String claimDescribe, StringBuilder resultData) {
        ClaimsDO claimsDO = new ClaimsDO();
        BeanUtil.copyProperties(claimsVO, claimsDO);

        //如果是补发商品，赠送礼品，赔付现金时，记录解决说明，否则记录自己的处理描述
        if (claimsDO.getClaimDescribe() == null) {
            claimsDO.setClaimDescribe(claimDescribe);
        }
        //处理出库时间
        if (StringUtil.notEmpty(claimsVO.getDeliveryTimeString())) {
            claimsDO.setDeliveryTime(DateUtil.getDateline(claimsVO.getDeliveryTimeString()));
        }
        //理赔单号
        String claimSn = this.claimSnPrefix + DateUtil.toString(DateUtil.getDateline(), "yyMMddHHmmss") + (new Random().nextInt(999) + 100);
        claimsDO.setClaimSn(claimSn);
        //申请人信息
        claimsDO.setApplyMemberId(clerk.getClerkId());
        claimsDO.setApplyMemberName(clerk.getClerkName());
        //申请理赔的时间
        claimsDO.setApplyTime(DateUtil.getDateline());
        //理赔状态：已提交
        claimsDO.setClaimStatus(ClaimsStatusEnum.SUBMIT.value());

        String claimType = claimsVO.getClaimType();

        if (claimType.equals(ClaimsTypeEnum.COMPENSATION_GOODS.value()) || claimType.equals(ClaimsTypeEnum.FREE_GIFT.value())) {
            //如果SkuList不为空，保存sku信息，计算理赔金额
            if (!CollectionUtils.isEmpty(claimsVO.getSkuList())) {
                List<ClaimSkusDTO> originalSkusDTOS = claimsVO.getSkuList();
                //1、根据skuId查询sku信息
                List<Integer> skuIds = originalSkusDTOS.stream().map(ClaimSkusDTO::getSkuId).collect(Collectors.toList());
                String str = StringUtil.implode(",", skuIds.toArray(new Integer[0]));
                String sql = "select * from es_goods_sku where sku_id in(" + str + ")";
                List<ClaimSkusDTO> claimSkusDTOList = this.daoSupport.queryForList(sql, ClaimSkusDTO.class);
                if(this.checkSkuList(originalSkusDTOS,claimSkusDTOList, resultData)){
                    return null;
                }

                // 把前端传过来的sku信息转成map
                Map<Integer, ClaimSkusDTO> skusDTOMap = originalSkusDTOS.stream().collect(Collectors.toMap(ClaimSkusDTO::getSkuId, Function.identity()));
                //理赔成本 sku的成本价*数量
                Double claimCost = 0.0;

                // 2、循环claimSkusDTOList，判断库存，添加购买数量和补发数量，计算理赔金额
                for (ClaimSkusDTO claimSku : claimSkusDTOList) {
                    ClaimSkusDTO paramSku = skusDTOMap.get(claimSku.getSkuId());
                    //补发数量
                    Integer reissueNum = paramSku.getCompensationNum();
                    //3、如果sku的可用库存 < 补发（或赠送）的商品数量，提示该商品库存不足；
                    if (reissueNum > 0 && claimSku.getEnableQuantity() < reissueNum) {
                        // throw new ServiceException(ExceptionClaimsCodeEnum.E616.value(), claimSku.getGoodsName() + "   " + ExceptionClaimsCodeEnum.E616.getDescription());
                        resultData.append(claimSku.getGoodsName() + " * " + reissueNum + "；");
                    }

                    if (claimSku.getCost() != null) {
                        claimCost = CurrencyUtil.add(claimCost, CurrencyUtil.mul(claimSku.getCost(), reissueNum));
                    }

                    //4、添加购买数量和补发数量
                    claimSku.setCompensationNum(reissueNum);
                    //购买数量
                    claimSku.setNum(paramSku.getNum());
                    //商品名,为了前端显示兼容name
                    claimSku.setName(claimSku.getGoodsName());
                }
                claimsDO.setClaimCost(claimCost);
                //5、保存sku信息
                claimsDO.setClaimSkus(JsonUtil.objectToJson(claimSkusDTOList));
            } else {
                return null;
            }
        } else if (claimType.equals(ClaimsTypeEnum.AFTERSALE_COUPON.value())) {
            if (claimsVO.getCouponId() != null) {
                //如果理赔方式为售后券，把售后券信息放进ClaimSkus
                CouponDO couponDO = couponManager.getModel(claimsVO.getCouponId());
                claimsDO.setClaimCost(couponDO.getCouponPrice());
                claimsDO.setClaimSkus(JsonUtil.objectToJson(couponDO));
            } else {
                return null;
            }

        }

        //如果cost是空说明是赔付金额或多退少补
        if (claimsDO.getClaimCost() == null) {
            claimsDO.setClaimCost(claimsDO.getClaimPrice());
        }
        return claimsDO;
    }

    // 检查补发商品是否有被删除的sku
    private boolean checkSkuList(List<ClaimSkusDTO> originalSkusDTOS,List<ClaimSkusDTO> claimSkusDTOList, StringBuilder resultData){
        if (CollectionUtils.isEmpty(claimSkusDTOList)) {
            List<String> skuNameList = originalSkusDTOS.stream().map(ClaimSkusDTO::getName).collect(Collectors.toList());
            resultData.append("商品（" + StringUtil.listToString(skuNameList, ",") + "）已被删除！");
            return true;
        }

        if(claimSkusDTOList.size() != originalSkusDTOS.size()){
            List<Integer> skuIds=claimSkusDTOList.stream().map(c->c.getSkuId()).collect(Collectors.toList());
            originalSkusDTOS=originalSkusDTOS.stream().filter(item -> !skuIds.contains(item.getSkuId())).collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(originalSkusDTOS)){
                List<String> skuNameList = originalSkusDTOS.stream().map(ClaimSkusDTO::getName).collect(Collectors.toList());
                resultData.append("商品（" + StringUtil.listToString(skuNameList, ",") + "）已被删除！");
                return true;
            }
        }
        return false;
    }

    /**
     * 获取店员信息
     */
    private Clerk getClerk(Integer MemberId) {
        //获取申请理赔单的店员信息
        Clerk clerk = clerkManager.getClerkByMemberId(MemberId);
        if (clerk == null) {
            throw new ServiceException(ExceptionClaimsCodeEnum.E614.value(), "获取店员信息异常");
        }
        return clerk;
    }

    private void sendMq(List<ClaimsDO> claimsDOList, String claimsStatus){
        ClaimsChangeMsg claimsChangeMsg = new ClaimsChangeMsg();
        claimsChangeMsg.setClaimsDOList(claimsDOList);
        claimsChangeMsg.setAuditEnum(claimsStatus);
        this.amqpTemplate.convertAndSend(AmqpExchange.CLAIMS_STATUS_CHANGE,
                AmqpExchange.CLAIMS_STATUS_CHANGE + "_ROUTING", claimsChangeMsg);
    }

    /**
     * es_claim表中所有的字段
     */
    private String sqlParam() {
        final String sqlParam = " c.claim_id,c.claim_sn,c.exception_id,c.exception_sn,c.order_sn,c.claim_status,c.claim_type,c.claim_skus,c.claim_price,c.claim_cost,c.claim_describe,\n" +
                "c.claim_time,c.claim_member_id,c.claim_member_name,c.apply_member_id,c.apply_member_name,c.audit_member_id,c.audit_member_name,c.audit_time,c.audit_describe, \n" +
                "c.apply_time,c.refund_reason,c.member_id,c.delivery_time \n";
        return sqlParam;
    }

}
